package com.atguigu.es;

import com.atguigu.es.vo.Product;
import com.google.gson.Gson;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@SpringBootTest
public class Demo02EsRestTemplate {

    /**用ElasticsearchRestTemplate客户端进行复杂查询*/
    @Autowired
    ElasticsearchRestTemplate restTemplate;

    //匹配查询、模糊查询、布尔查询、分页查询、过滤查询、高亮查询
    @Test
    void testQuery() {
        //builder构建查询条件
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //匹配查询：title中包含小米手机
        /*builder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));*/
        //因为要测试模糊查询，先注掉

        //模糊查询:Fuzziness.TWO指偏移量为2 ,如果分的词该两次可以变为小米，文档就可以匹配。默认的偏差量为一。
        builder.withQuery(QueryBuilders.fuzzyQuery("title","小米").fuzziness(Fuzziness.TWO));

        //布尔查询
        builder.withQuery(QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("attr.brand.keyword","小米"))  //布尔查询的第一个条件
                .must(QueryBuilders.rangeQuery("price").lte(3999).gte(1999)));  //布尔查询的第二个条件

        //分页查询
        builder.withPageable(PageRequest.of(1,5));

        //过滤查询的字段
        builder.withFields("id","title","price","att.category");//查询结果里,这里面的字段都会有值，而其他的字段的值，我们都不查

        //高亮查询,field()里面的字段会被高亮显示，preTag表示给查询结果的高亮字段加前缀，postTag表示给查询结果的高亮字段加后缀，
        builder.withHighlightBuilder(new HighlightBuilder().field("title").preTags("<em>").postTags("</em>"));

        //发起查询
        SearchHits<Product> searchResult = restTemplate.search(builder.build(), Product.class);

        //解析查询到的结果集
        searchResult.getSearchHits().forEach(searchHit -> {
            Product product = searchHit.getContent();

            //配置高亮
            List<String> titles = searchHit.getHighlightField("title");//没解析一个文档是时，获取它的高亮字段
            //System.out.println("高亮字段：" + titles);
            if(!CollectionUtils.isEmpty(titles)) {
                product.setTitle(titles.get(0));//将高亮字段的值设置给数据源(product)
            }
            System.out.println(product);
        });
    }

    //聚合查询
    @Test
    void testQuery2() {
        //builder构建查询条件
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //高亮查询
        builder.withHighlightBuilder(new HighlightBuilder().field("title").preTags("<em>").postTags("</em>"));

        //聚合查询：使品牌聚合为桶，桶内再次使用分类聚合为桶，在分类桶中进行度量统计平均价格
        builder.addAggregation(AggregationBuilders.terms("品牌").field("attr.brand.keyword")  //term表示根据attr.brand字段聚合为桶(keyword表示不分词),桶名字叫“品牌”，
               .subAggregation(AggregationBuilders.terms("分类").field("attr.category.keyword")  //品牌桶内聚合为分类桶
                                    .subAggregation(AggregationBuilders.avg("平均价格分类").field("price"))));    //对分类桶 进行度量

        //发起查询
        SearchHits<Product> searchResult = restTemplate.search(builder.build(), Product.class);

        //解析查询到的结果集(hit)
        searchResult.getSearchHits().forEach(searchHit -> {
            Product product = searchHit.getContent();
            //配置高亮
            List<String> titles = searchHit.getHighlightField("title");//没解析一个文档是时，获取它的高亮字段
            //System.out.println("高亮字段：" + titles);
            if(!CollectionUtils.isEmpty(titles)) {
                product.setTitle(titles.get(0));//将高亮字段的值设置给数据源(product)
            }
            System.out.println(product);
        });

        //解析查询到的Aggregation 里面包括桶(bucket)和度量。聚合查询每一步都要先有Aggregation，无论聚合还是度量
        //聚合时：结果有可能是分组(聚合)结果(ParsedStringTerms类接收)，也有可能是统计(度量)结果(ParsedAvg),这里经测试，是分组结果
        ParsedStringTerms brands = searchResult.getAggregations().get("品牌");//根据桶的名称 从查询的json结果 拿到最外层的桶，再从中获取桶的内容
        System.out.println("最外层的桶的名称：" + brands.getName());
        //根据最外层的桶，获取到内层所有的桶
        brands.getBuckets().forEach(bucket ->{
            System.out.println(brands.getName() + ":" + bucket.getKey() + "," + bucket.getDocCount());//品牌桶的内容,和该内容的数量
            ParsedStringTerms categories = bucket.getAggregations().get("分类");//从品牌桶里获取到 分类桶
            categories.getBuckets().forEach(category ->{
                System.out.println(category.getKey() + "," + category.getDocCount()); //分类桶的内容,和该内容的数量
                ParsedAvg avgPrice = category.getAggregations().get("分类平均价格"); //分类桶的度量
                System.out.println(avgPrice.getName() + "," + avgPrice.getValue()); //分类桶 度量的名称 和内容
            });
        });


    }

    /**RestHighLevelClient客户端进行复杂查询*/
    @Autowired
    RestHighLevelClient restHighLevelClient;

    Gson gson = new Gson();

    @Test
    void testHighLevelClient() throws IOException {
        //构建查询对象
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //构建查询条件
        sourceBuilder.query(QueryBuilders.matchQuery("title","华为手机"));//匹配查询
        sourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<b style='color:red'>").postTags("</b>")); //高亮查询
        searchRequest.source(sourceBuilder);

        //发起查询
        SearchResponse searchResult = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //解析查询到的结果集
        SearchHit[] hits = searchResult.getHits().getHits();
        for (SearchHit hit : hits) {
            System.out.println(hit.getSourceAsString());
            Product product = gson.fromJson(hit.getSourceAsString(), Product.class);//将解析到的结果集转成product对象
            product.setId(Long.parseLong(hit.getId()));//hit.getSourceAsString()的结果不带id，我们需要手动添加

            //配置高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField highlightField = highlightFields.get("title");
            String str = highlightField.getFragments().toString();
            product.setTitle(str);
            System.out.println(product);
        }



    }



}
